﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Firefly.Framework.Input
{
    public class GamePadManager : HumanInterfaceDevice<GamePadState>, IHumanInterfaceDevice<GamePadState>
    {
        public PlayerIndex PlayerIndex { get; private set; }
        public List<GamePadButton> CurrentButtonsPressed { get; private set; }
        public List<GamePadButton> PreviousButtonsPressed { get; private set; }
        public List<GamePadDPadButton> CurrentDPadButtonsPressed { get; private set; }
        public List<GamePadDPadButton> PreviousDPadButtonsPressed { get; private set; }
        public Vector2 CurrentLeftThumbStickPosition
        {
            get { return new Vector2(CurrentState.ThumbSticks.Left.X, CurrentState.ThumbSticks.Left.Y); }
        }
        public Vector2 PreviousLeftThumbStickPosition
        {
            get { return new Vector2(PreviousState.ThumbSticks.Left.X, PreviousState.ThumbSticks.Left.Y); }
        }
        public Vector2 LeftThumbStickPositionDelta
        {
            get { return new Vector2(CurrentState.ThumbSticks.Left.X - PreviousState.ThumbSticks.Left.X, CurrentState.ThumbSticks.Left.Y - PreviousState.ThumbSticks.Left.Y); }
        }
        public Vector2 CurrentRightThumbStickPosition
        {
            get { return new Vector2(CurrentState.ThumbSticks.Right.X, CurrentState.ThumbSticks.Right.Y); }
        }
        public Vector2 PreviousRightThumbStickPosition
        {
            get { return new Vector2(PreviousState.ThumbSticks.Right.X, PreviousState.ThumbSticks.Right.Y); }
        }
        public Vector2 RightThumbStickPositionDelta
        {
            get { return new Vector2(CurrentState.ThumbSticks.Right.X - PreviousState.ThumbSticks.Right.X, CurrentState.ThumbSticks.Right.Y - PreviousState.ThumbSticks.Right.Y); }
        }
        public float CurrentLeftTriggerPosition
        {
            get { return CurrentState.Triggers.Left; }
        }
        public float PreviousLeftTriggerPosition
        {
            get { return PreviousState.Triggers.Left; }
        }
        public float LeftTriggerPositionDelta
        {
            get { return CurrentState.Triggers.Left - PreviousState.Triggers.Left; }
        }
        public float CurrentRightTriggerPosition
        {
            get { return CurrentState.Triggers.Right; }
        }
        public float PreviousRightTriggerPosition
        {
            get { return PreviousState.Triggers.Right; }
        }
        public float RightTriggerPositionDelta
        {
            get { return CurrentState.Triggers.Right - PreviousState.Triggers.Right; }
        }

        public GamePadManager(PlayerIndex playerIndex)
        {
            PlayerIndex = playerIndex;
            DeviceType = DeviceType.GamePad;
            CurrentState = GamePad.GetState(PlayerIndex);
            CurrentButtonsPressed = new List<GamePadButton>();
            PreviousButtonsPressed = new List<GamePadButton>();
            CurrentDPadButtonsPressed = new List<GamePadDPadButton>();
            PreviousDPadButtonsPressed = new List<GamePadDPadButton>();
            UpdateButtons();
            UpdateDPadButtons();
        }

        public bool IsButtonUp(GamePadButton button)
        {
            return (ResolveButtonState(CurrentState, button) == ButtonState.Released) ? true : false;
        }

        public bool WasButtonUp(GamePadButton button)
        {
            return (ResolveButtonState(PreviousState, button) == ButtonState.Released) ? true : false;
        }

        public bool IsButtonDown(GamePadButton button)
        {
            return (ResolveButtonState(CurrentState, button) == ButtonState.Pressed) ? true : false;
        }

        public bool WasButtonDown(GamePadButton button)
        {
            return (ResolveButtonState(PreviousState, button) == ButtonState.Pressed) ? true : false;
        }

        public bool WasButtonPressed(GamePadButton button)
        {
            return (WasButtonUp(button) && IsButtonUp(button)) ? true : false;
        }

        public bool WasButtonReleased(GamePadButton button)
        {
            return (WasButtonDown(button) && IsButtonUp(button)) ? true : false;
        }

        public bool WasButtonHeld(GamePadButton button)
        {
            return (WasButtonDown(button) && IsButtonDown(button)) ? true : false;
        }

        public bool IsDPadButtonUp(GamePadDPadButton button)
        {
            return (ResolveDPadState(CurrentState, button) == ButtonState.Released) ? true : false;
        }

        public bool WasDPadButtonUp(GamePadDPadButton button)
        {
            return (ResolveDPadState(PreviousState, button) == ButtonState.Released) ? true : false;
        }

        public bool IsDPadButtonDown(GamePadDPadButton button)
        {
            return (ResolveDPadState(CurrentState, button) == ButtonState.Pressed) ? true : false;
        }

        public bool WasDPadButtonDown(GamePadDPadButton button)
        {
            return (ResolveDPadState(PreviousState, button) == ButtonState.Pressed) ? true : false;
        }

        public bool WasDPadButtonPressed(GamePadDPadButton button)
        {
            return (WasDPadButtonUp(button) && IsDPadButtonUp(button)) ? true : false;
        }

        public bool WasDPadButtonReleased(GamePadDPadButton button)
        {
            return (WasDPadButtonDown(button) && IsDPadButtonUp(button)) ? true : false;
        }

        public bool WasDPadButtonHeld(GamePadDPadButton button)
        {
            return (WasDPadButtonDown(button) && IsDPadButtonDown(button)) ? true : false;
        }

        private static ButtonState ResolveButtonState(GamePadState state, GamePadButton button)
        {
            GamePadButtons buttonStates = state.Buttons;

            switch (button)
            {
                case GamePadButton.A:
                    return buttonStates.A;
                case GamePadButton.B:
                    return buttonStates.B;
                case GamePadButton.Back:
                    return buttonStates.Back;
                case GamePadButton.LeftShoulder:
                    return buttonStates.LeftShoulder;
                case GamePadButton.LeftStick:
                    return buttonStates.LeftStick;
                case GamePadButton.RightShoulder:
                    return buttonStates.RightShoulder;
                case GamePadButton.RightStick:
                    return buttonStates.RightStick;
                case GamePadButton.Start:
                    return buttonStates.Start;
                case GamePadButton.X:
                    return buttonStates.X;
                case GamePadButton.Y:
                    return buttonStates.Y;
                case GamePadButton.BigButton:
                    return buttonStates.BigButton;
                default:
                    return ButtonState.Released;
            }
        }

        private static ButtonState ResolveDPadState(GamePadState state, GamePadDPadButton button)
        {
            GamePadDPad buttonStates = state.DPad;

            switch (button)
            {
                case GamePadDPadButton.Down:
                    return buttonStates.Down;
                case GamePadDPadButton.Left:
                    return buttonStates.Left;
                case GamePadDPadButton.Right:
                    return buttonStates.Right;
                case GamePadDPadButton.Up:
                    return buttonStates.Up;
                default:
                    return ButtonState.Released;
            }
        }

        private void UpdateButtons()
        {
            PreviousButtonsPressed = CurrentButtonsPressed;
            CurrentButtonsPressed.Clear();

            foreach (GamePadButton button in Enum.GetValues(typeof(GamePadButton)))
            {
                if (ResolveButtonState(CurrentState, button) == ButtonState.Pressed)
                {
                    CurrentButtonsPressed.Add(button);
                }
            }
        }

        private void UpdateDPadButtons()
        {
            PreviousDPadButtonsPressed = CurrentDPadButtonsPressed;
            CurrentDPadButtonsPressed.Clear();

            foreach (GamePadDPadButton button in Enum.GetValues(typeof(GamePadDPadButton)))
            {
                if (ResolveDPadState(CurrentState, button) == ButtonState.Pressed)
                {
                    CurrentDPadButtonsPressed.Add(button);
                }
            }
        }

        #region IHumanInterfaceDevice Members

        public override void RegisterInputMap(IInputMap map)
        {
            (map as IHasInputManager<GamePadManager>).Manager = this;
            base.RegisterInputMap(map);
        }

        #endregion

        #region IUpdateable Members

        public override void Update(GameTime gameTime)
        {
            if (Enabled)
            {
                PreviousState = CurrentState;
                CurrentState = GamePad.GetState(PlayerIndex);
                UpdateButtons();
                UpdateDPadButtons();

                foreach (IInputMap map in InputMaps)
                {
                    if (map.InputMapType == InputMapType.GamePadButtonMap || map.InputMapType == InputMapType.GamePadDPadMap || map.InputMapType == InputMapType.GamePadThumbStickMap || map.InputMapType == InputMapType.GamePadTriggerMap)
                    {
                        map.Update(gameTime);
                    }
                }

                if (CurrentState != PreviousState)
                {
                    NotifyStateChanged(new StateChangedEventArgs<GamePadState>(CurrentState));
                }
            }
        }

        #endregion
    }
}
